Allow configuratin ar/ld for rustc
authorAlex Crichton <alex@alexcrichton.com>
Mon, 14 Jul 2014 17:57:04 +0000 (10:57 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Mon, 14 Jul 2014 18:28:40 +0000 (11:28 -0700)
This adds a new .cargo/config option which allows configuring the ar and linker
tools that rustc invokes. This should aid in any cross-compilation attempts.

src/cargo/ops/cargo_compile.rs
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/util/config.rs
tests/test_cargo_cross_compile.rs

index 44fba5c8038aaf2fa01dd13f6d7945b198978c42..b4d91f1f9836fb1ad6fe8bb860ceb6a584250465 100644 (file)
 //!
 
 use std::os;
-use util::config::{Config, ConfigValue};
-use core::{MultiShell, Source, SourceId, PackageSet, Target, PackageId, resolver};
+use std::collections::HashMap;
+
 use core::registry::PackageRegistry;
+use core::{MultiShell, Source, SourceId, PackageSet, Target, PackageId, resolver};
 use ops;
 use sources::{PathSource};
-use util::{CargoResult, Wrap, config, internal, human};
+use util::config::{Config, ConfigValue};
+use util::{CargoResult, Wrap, config, internal, human, ChainError};
 
 pub struct CompileOptions<'a> {
     pub update: bool,
@@ -57,7 +59,8 @@ pub fn compile(manifest_path: &Path,
         try!(shell.warn(format!("unused manifest key: {}", key)));
     }
 
-    let override_ids = try!(source_ids_from_config());
+    let user_configs = try!(config::all_configs(os::getcwd()));
+    let override_ids = try!(source_ids_from_config(&user_configs));
     let source_ids = package.get_source_ids();
 
     let (packages, resolve) = {
@@ -85,6 +88,7 @@ pub fn compile(manifest_path: &Path,
     }).collect::<Vec<&Target>>();
 
     let mut config = try!(Config::new(*shell, update, jobs, target));
+    try!(scrape_target_config(&mut config, &user_configs));
 
     try!(ops::compile_targets(env.as_slice(), targets.as_slice(), &package,
          &PackageSet::new(packages.as_slice()), &resolve, &mut config));
@@ -103,23 +107,57 @@ pub fn compile(manifest_path: &Path,
     Ok(test_executables)
 }
 
-fn source_ids_from_config() -> CargoResult<Vec<SourceId>> {
-    let configs = try!(config::all_configs(os::getcwd()));
-
+fn source_ids_from_config(configs: &HashMap<String, config::ConfigValue>)
+                          -> CargoResult<Vec<SourceId>> {
     debug!("loaded config; configs={}", configs);
 
     let config_paths = configs.find_equiv(&"paths").map(|v| v.clone());
     let config_paths = config_paths.unwrap_or_else(|| ConfigValue::new());
 
-    let paths: Vec<Path> = match *config_paths.get_value() {
-        config::String(_) => return Err(internal("The path was configured as \
-                                                  a String instead of a List")),
-        config::Table(_) => return Err(internal("The path was configured as \
-                                                 a Table instead of a List")),
-        config::List(ref list) => {
-            list.iter().map(|path| Path::new(path.as_slice())).collect()
-        }
+    let paths = try!(config_paths.list().chain_error(|| {
+        internal("invalid configuration for the key `path`")
+    }));
+
+    Ok(paths.iter().map(|p| SourceId::for_path(&Path::new(p.as_slice()))).collect())
+}
+
+fn scrape_target_config(config: &mut Config,
+                        configs: &HashMap<String, config::ConfigValue>)
+                        -> CargoResult<()> {
+    let target = match configs.find_equiv(&"target") {
+        None => return Ok(()),
+        Some(target) => try!(target.table().chain_error(|| {
+            internal("invalid configuration for the key `target`")
+        })),
+    };
+    let target = match config.target() {
+        None => target,
+        Some(triple) => match target.find_equiv(&triple) {
+            None => return Ok(()),
+            Some(target) => try!(target.table().chain_error(|| {
+                internal(format!("invalid configuration for the key \
+                                  `target.{}`", triple))
+            })),
+        },
     };
 
-    Ok(paths.iter().map(|p| SourceId::for_path(p)).collect())
+    match target.find_equiv(&"ar") {
+        None => {}
+        Some(ar) => {
+            config.set_ar(try!(ar.string().chain_error(|| {
+                internal("invalid configuration for key `ar`")
+            })).to_string());
+        }
+    }
+
+    match target.find_equiv(&"linker") {
+        None => {}
+        Some(linker) => {
+            config.set_linker(try!(linker.string().chain_error(|| {
+                internal("invalid configuration for key `ar`")
+            })).to_string());
+        }
+    }
+
+    Ok(())
 }
index 300f665358cee3f2ebf1c713f59f1abab82b8c94..989889eacf0762597b5ce6fdd7c81f64bb7b6018 100644 (file)
@@ -278,12 +278,21 @@ fn build_base_args(into: &mut Args,
     into.push("--out-dir".to_string());
     into.push(cx.dest(plugin).display().to_string());
 
-    match cx.config.target() {
-        Some(target) if !plugin => {
-            into.push("--target".to_string());
-            into.push(target.to_string());
+    if !plugin {
+        fn opt(into: &mut Vec<String>, key: &str, prefix: &str,
+               val: Option<&str>) {
+            match val {
+                Some(val) => {
+                    into.push(key.to_string());
+                    into.push(format!("{}{}", prefix, val));
+                }
+                None => {}
+            }
         }
-        _ => {}
+
+        opt(into, "--target", "", cx.config.target());
+        opt(into, "-C", "ar=", cx.config.ar());
+        opt(into, "-C", "linker=", cx.config.linker());
     }
 }
 
index 7525e7787910ed370cd07f941ae29acd245cd15b..a8e71d3e34b4a20a3737c7d46257ebcb627a818f 100644 (file)
@@ -13,6 +13,8 @@ pub struct Config<'a> {
     shell: &'a mut MultiShell,
     jobs: uint,
     target: Option<String>,
+    linker: Option<String>,
+    ar: Option<String>,
 }
 
 impl<'a> Config<'a> {
@@ -32,6 +34,8 @@ impl<'a> Config<'a> {
             shell: shell,
             jobs: jobs.unwrap_or(os::num_cpus()),
             target: target,
+            ar: None,
+            linker: None,
         })
     }
 
@@ -58,6 +62,17 @@ impl<'a> Config<'a> {
     pub fn target<'a>(&'a self) -> Option<&'a str> {
         self.target.as_ref().map(|t| t.as_slice())
     }
+
+    pub fn set_ar(&mut self, ar: String) { self.ar = Some(ar); }
+
+    pub fn set_linker(&mut self, linker: String) { self.linker = Some(linker); }
+
+    pub fn linker<'a>(&'a self) -> Option<&'a str> {
+        self.linker.as_ref().map(|t| t.as_slice())
+    }
+    pub fn ar<'a>(&'a self) -> Option<&'a str> {
+        self.ar.as_ref().map(|t| t.as_slice())
+    }
 }
 
 #[deriving(Eq,PartialEq,Clone,Encodable,Decodable)]
@@ -161,6 +176,30 @@ impl ConfigValue {
 
         Ok(())
     }
+
+    pub fn string<'a>(&'a self) -> CargoResult<&'a str> {
+        match self.value {
+            Table(_) => Err(internal("expected a string, but found a table")),
+            List(_) => Err(internal("expected a string, but found a list")),
+            String(ref s) => Ok(s.as_slice()),
+        }
+    }
+
+    pub fn table<'a>(&'a self) -> CargoResult<&'a HashMap<String, ConfigValue>> {
+        match self.value {
+            String(_) => Err(internal("expected a table, but found a string")),
+            List(_) => Err(internal("expected a table, but found a list")),
+            Table(ref table) => Ok(table),
+        }
+    }
+
+    pub fn list<'a>(&'a self) -> CargoResult<&'a [String]> {
+        match self.value {
+            String(_) => Err(internal("expected a list, but found a string")),
+            Table(_) => Err(internal("expected a list, but found a table")),
+            List(ref list) => Ok(list.as_slice()),
+        }
+    }
 }
 
 impl ConfigValueValue {
index 9483e82d0995d2e03e2397f0d355eeb0ddad7d84..e049cfd1445f81fd877a09f78bfb4e6a1166ffc9 100644 (file)
@@ -4,8 +4,10 @@
 #![cfg(target_os = "macos")]
 
 use std::os;
+use std::path;
 
 use support::{project, execs, basic_bin_manifest};
+use support::{RUNNING, COMPILING};
 use hamcrest::{assert_that, existing_file};
 use cargo::util::process;
 
@@ -230,3 +232,39 @@ test!(plugin_to_the_max {
       process(foo.target_bin(target, "main")),
       execs().with_status(0));
 })
+
+test!(linker_and_ar {
+    let target = alternate();
+    let p = project("foo")
+        .file(".cargo/config", format!(r#"
+            [target.{}]
+            ar = "my-ar-tool"
+            linker = "my-linker-tool"
+        "#, target).as_slice())
+        .file("Cargo.toml", basic_bin_manifest("foo").as_slice())
+        .file("src/foo.rs", r#"
+            use std::os;
+            fn main() {
+                assert_eq!(os::consts::ARCH, "x86");
+            }
+        "#);
+
+    assert_that(p.cargo_process("cargo-build").arg("--target").arg(target)
+                                              .arg("-v"),
+                execs().with_status(101)
+                       .with_stdout(format!("\
+{running} `rustc src/foo.rs --crate-name foo --crate-type bin \
+    --out-dir {dir}{sep}target{sep}{target} \
+    --target {target} \
+    -C ar=my-ar-tool -C linker=my-linker-tool \
+    -L {dir}{sep}target{sep}{target} \
+    -L {dir}{sep}target{sep}{target}{sep}deps`
+{compiling} foo v0.5.0 (file:{dir})
+",
+                            running = RUNNING,
+                            compiling = COMPILING,
+                            dir = p.root().display(),
+                            target = target,
+                            sep = path::SEP,
+                            ).as_slice()));
+})